/**
 * 
 */
package optimization.naturalOptimization.evolutionaryAlgorithm;

import java.util.ArrayList;

import optimization.naturalOptimization.NaturalOptimization;
import optimization.naturalOptimization.evolutionaryAlgorithm.evolutionaryOperator.EvolutionaryOperator;
import optimization.naturalOptimization.population.Population;
import optimization.naturalOptimization.population.individual.Individual;

/**
 * @author Kevin Wagner
 * @version 1.0
 */
public abstract class EvolutionaryAlgorithm extends NaturalOptimization {

	private ArrayList<EvolutionaryOperator> pipeline = new ArrayList<EvolutionaryOperator>();
	private Population parentPopulation = new Population(this);
	private Population progenyPopulation = new Population(this);

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#performStep()
	 */
	@Override
	public boolean performStep() {
		for (int i = 0; i < pipeline.size(); i++) {
			pipeline.get(i).work();
		}
		setResult(getCurrentBestFitness());
		return true;
	}

	/**
	 * Adds a new evolutionary operator the the pipeline.
	 * 
	 * @param operator
	 *            new operator for the pipeline
	 */
	public void addOperator(EvolutionaryOperator operator) {
		pipeline.add(operator);
	}

	/**
	 * Returns the number of progeny.
	 * 
	 * @return the number of progeny
	 */
	public int getLambda() {
		return Integer.valueOf(getProperties().getProperty("lambda", "1"));
	}

	/**
	 * Returns the number of parents.
	 * 
	 * @return the number of parents
	 */
	public int getMu() {
		return Integer.valueOf(getProperties().getProperty("mu", "1"));
	}

	/**
	 * Returns the parent population.
	 * 
	 * @return the parentPopulation
	 */
	public Population getParentPopulation() {
		return parentPopulation;
	}

	/**
	 * Returns the progeny population.
	 * 
	 * @return the progenyPopulation
	 */
	public Population getProgenyPopulation() {
		return progenyPopulation;
	}

	/**
	 * Returns the Number of parents that produce one offspring.
	 * 
	 * @return number of parents to produce one offspring
	 */
	public int getRho() {
		return Integer.valueOf(getProperties().getProperty("rho", "1"));
	}

	/**
	 * Sets the parent population.
	 * 
	 * @param parentPopulation
	 *            the new parent Population
	 */
	public void setParentPopulation(Population parentPopulation) {
		this.parentPopulation = parentPopulation;
	}

	/**
	 * Sets the progeny population.
	 * 
	 * @param progenyPopulation
	 *            the new progeny Population
	 */
	public void setProgenyPopulation(Population progenyPopulation) {
		this.progenyPopulation = progenyPopulation;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#getParameters()
	 */
	public ArrayList<String> getParameters() {
		ArrayList<String> parameters = super.getParameters();
		parameters.add("mu");
		parameters.add("lambda");
		parameters.add("rho");
		return parameters;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see optimization.naturalOptimization.NaturalOptimization#
	 * getParameterConfiguration()
	 */
	public ArrayList<String> getParameterConfiguration() {
		ArrayList<String> configuration = super.getParameterConfiguration();
		configuration.add(getMu() + "");
		configuration.add(getLambda() + "");
		configuration.add(getRho() + "");
		return configuration;
	}

	/**
	 * Returns the current best fitness.
	 * 
	 * @return fitness value
	 */
	@SuppressWarnings("rawtypes")
	protected double[] getCurrentBestFitness() {
		double[] results=new double[4];
		double best = ((Individual) parentPopulation.getAllIndividuals().get(0))
				.getPositionFitness();
		double worst = ((Individual) parentPopulation.getAllIndividuals().get(0))
				.getPositionFitness();
		double mean = ((Individual) parentPopulation.getAllIndividuals().get(0))
				.getPositionFitness();
		double std=0.0;
		for (int i = 1; i < parentPopulation.getNumberOfIndividuals(); i++) {
			Individual current = (Individual) parentPopulation.getAllIndividuals().get(i);
			if (getFitness().compare(best, current.getPositionFitness()) == 2) {
				best = current.getPositionFitness();
			}
			if (getFitness().compare(worst, current.getPositionFitness()) == 1) {
				worst = current.getPositionFitness();
			}
			mean+=current.getPositionFitness();
		}
		mean/=parentPopulation.getNumberOfIndividuals();
		for (int i = 1; i < parentPopulation.getNumberOfIndividuals(); i++) {
			Individual current = (Individual) parentPopulation.getAllIndividuals().get(i);
			std+=Math.pow((current.getPositionFitness()-mean), 2.0);
		}
		std/=(parentPopulation.getNumberOfIndividuals()-1);
		std=Math.sqrt(std);
		results[0]=best;
		results[1]=worst;
		results[2]=mean;
		results[3]=std;
		return results;
	}

}
